Skip to main content

Structural Design Patterns

Pattern yang berkaitan dengan bagaimana kelas dan objek disusun untuk membentuk struktur yang lebih besar.


Adapter

Membuat dua interface yang tidak kompatibel bisa bekerja bersama — tanpa mengubah salah satunya.

4 Peran Utama

PeranDeskripsiContoh
ClientProyek yang membutuhkan tipe tertentuPaymentService
Target InterfaceInterface yang diharapkan oleh ClientPayment
AdapteeKelas yang sudah ada dan tidak cocok dengan interface-nyaGopay
AdapterJembatan yang menghubungkan Adaptee ke ClientGopayToPaymentAdapter

PaymentService hanya menerima objek yang mengimplementasikan Payment (dengan method pay()). PayPal dan Stripe tidak masalah — tapi Gopay menggunakan method yang sama sekali berbeda: payWithBalance().

Explorer
src
problem1-adapter
model
payment.ts
paypal.ts
stripe.ts
gopay.ts
adapter
GopayToPayment.new.ts
service
paymentService.ts
demo.ts
src/problem1-adapter/adapter/GopayToPayment.new.ts
// Adapter
// 1. Menyimpan object Adaptee sebagai attribute
// 2. Implementasi / extend target interface

import { Gopay } from "../model/gopay";
import { Payment } from "../model/payment";

export class GopayToPaymentAdapter implements Payment {
private _gopayPaymentService: Gopay;

constructor(gopayPaymentService: Gopay) {
this._gopayPaymentService = gopayPaymentService;
}

pay(amount: number): void {
this._gopayPaymentService.payWithBalance(amount);
}
}
Masalah — Interface tidak kompatibel
// model/gopay.ts — Adaptee yang tidak kompatibel
export class Gopay {
public payWithBalance(amount: number) {
console.log(`Balance withdraw ${amount} from gopay`);
}
}

// ❌ Gopay tidak bisa langsung dipakai — nama method berbeda
const paymentService = new PaymentService();
paymentService.processPayment(new Gopay(), 1000000); // type error
Solusi — Adapter menjembatani keduanya
// adapter/GopayToPayment.new.ts
// 1. Simpan object Adaptee sebagai attribute
// 2. Implementasi target interface

import { Gopay } from "../model/gopay";
import { Payment } from "../model/payment";

export class GopayToPaymentAdapter implements Payment {
private _gopayPaymentService: Gopay;

constructor(gopayPaymentService: Gopay) {
this._gopayPaymentService = gopayPaymentService;
}

pay(amount: number): void {
this._gopayPaymentService.payWithBalance(amount); // terjemahkan
}
}

// ✅ Sekarang Gopay bisa dipakai lewat Adapter
const paymentService = new PaymentService();
paymentService.processPayment(new PayPal(), 1000000); // native
paymentService.processPayment(new Stripe(), 1000000); // native
paymentService.processPayment(
new GopayToPaymentAdapter(new Gopay()),
1000000 // via adapter
);

Aturan utama: Baik Client (PaymentService) maupun Adaptee (Gopay) tidak perlu diubah. Adapter menangani semuanya di antara mereka.

Kapan Digunakan

  • Kamu ingin menggunakan kelas atau library yang ada, tapi interface-nya tidak cocok dengan proyekmu
  • Kamu tidak bisa memodifikasi kelas aslinya (misal: library pihak ketiga)
  • Kamu perlu membuat beberapa kelas yang tidak kompatibel bekerja dengan interface yang sama

Facade

Menyediakan interface yang disederhanakan untuk sebuah subsistem yang kompleks.

Sebuah home theater memiliki banyak komponen — masing-masing dengan method-nya sendiri. Client harus mengatur dan membongkar semuanya secara manual dengan urutan yang benar.

Explorer
src
problem2-facade
model
dvdPlayer.ts
projector.ts
soundSystem.ts
lights.ts
facade
MovieFacade.new.ts
demo.before.ts
demo.ts
src/problem2-facade/facade/MovieFacade.new.ts
// Facade
// 1. Attribute: object yang digunakan
// 2. Methods: function yang dibutuhkan

import { DvdPlayer } from "../model/dvdPlayer";
import { Lights } from "../model/lights";
import { Projector } from "../model/projector";
import { SoundSystem } from "../model/soundSystem";

export class MovieFacade {
private _dvd: DvdPlayer;
private _projector: Projector;
private _sound: SoundSystem;
private _lights: Lights;

constructor() {
this._dvd = new DvdPlayer();
this._projector = new Projector();
this._sound = new SoundSystem();
this._lights = new Lights();
}

public movieStart() {
this._lights.dim(30);
this._projector.on();
this._projector.wideScreenMode();
this._sound.on();
this._sound.setVolume(70);
this._dvd.on();
this._dvd.play("Inception");
}

public movieStop() {
this._dvd.stop();
this._dvd.off();
this._sound.off();
this._projector.off();
this._lights.on();
}
}
Masalah — Client mengatur segalanya
// demo.before.ts — client harus tahu urutan yang tepat
const dvd = new DvdPlayer();
const projector = new Projector();
const sound = new SoundSystem();
const lights = new Lights();

lights.dim(30);
projector.on();
projector.wideScreenMode();
sound.on();
sound.setVolume(70);
dvd.on();
dvd.play("Inception");

console.log("Watching movie...");

dvd.stop();
dvd.off();
sound.off();
projector.off();
lights.on();
Solusi — Facade menyembunyikan kompleksitas
// demo.ts — client hanya berbicara dengan Facade
import { MovieFacade } from "./facade/MovieFacade.new";

const movieFacade = new MovieFacade();

movieFacade.movieStart(); // semua setup diurus di dalam
console.log("Watching movie...");

movieFacade.movieStop(); // semua teardown diurus di dalam

Aturan utama: Client tidak perlu tahu apa yang terjadi di balik layar. Facade mengurus urutannya.

Kapan Digunakan

  • Sebuah subsistem memiliki banyak kelas dan client harus berinteraksi dengan semuanya
  • Kamu ingin menyediakan titik masuk yang bersih dan sederhana ke sistem yang kompleks
  • Kamu ingin mengurangi ketergantungan antara client dan internal subsistem

Ringkasan

PatternMasalah yang DipecahkanMekanisme Utama
AdapterInterface yang tidak kompatibel antar kelasWrapper class yang menerjemahkan satu interface ke interface lain
FacadeSubsistem yang kompleks dengan banyak komponenSatu kelas yang mengekspos method tingkat tinggi yang sederhana